﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace DienLib.SummitLib
{
    /// <summary>
    /// Wrapper for UnitTextBox Summit UserControl
    /// </summary>
    public class UltraTreeView
    {
        public enum UltraTreeAction // Add Infragistic.Win.DLL from ProgramFile/TIMS/JOBS & Print ToString Whatever Enum you want. See UltraTreeActionTest Below
        {
            //AboveCell = 56,  //Activates the same cell in the previous logical row.
            //BelowCell = 55,  //Activates the same cell in the next logical row.
            //CheckOptionButton = 48,  //Checks the currently active node's option button
            //ClearAllSelectedNodes = 46,  //Clears all the currently selected nodes
            CollapseNode = 40,   //Collapse the Active Node
            //Copy = 50,   //Copies the selected nodes to the clipboard
            //Cut = 49,    //Cuts the selected nodes to the clipboard
            //DeactivateCell = 71, //Deactivates the UltraTree.ActiveCell, while maintaining the UltraTree.ActiveNode. This makes it possible to redirect the keyboard focus to the ActiveNode, so that navigational keys will apply to nodes rather than cells.
            //DeleteNodes = 37,    //Deletes all Selected Nodes
            //EnterEditMode = 24,  //Enter Edit Mode
            //EnterEditModeOnCell = 65,    //Enters edit mode on the UltraTree.ActiveCell
            //ExitEditMode = 44,   //Exit Edit Mode
            //ExitEditModeAndSave = 45,    //Exit Edit Mode and save changes
            //ExitEditModeOnCellSaveChanges = 66,  //Ends the edit mode session for the UltraTree.ActiveCell, applying changes to the cell's value.
            ExpandAllNode = 32,  //Expand Active Node and its descendants
            ExpandNode = 30, //Expand the Active Node
            //FirstCellInNode = 57,    //Activates the cell in the first logical column for the UltraTreeNode which contains the UltraTree.ActiveCell.
            //FirstCellInNodesCollection = 59, //Activates the cell in the first logical column for the first visible UltraTreeNode in the same collection as the node which contains the UltraTree.ActiveCell.
            //FirstCellInTree = 61,    //Activates the cell in the first logical column for the first visible UltraTreeNode displayed by the UltraTree control.
            FirstNode = 31,  //Move to the First Node In Tree
            FirstNodeNoSelect = 15,  //Move to First Node without altering the Selection
            //LastCellInNode = 58, //Activates the cell in the last logical column for the UltraTreeNode which contains the UltraTree.ActiveCell.
            //LastCellInNodesCollection = 60,  //Activates the cell in the last logical column for the last visible UltraTreeNode in the same collection as the node which contains the UltraTree.ActiveCell.
            //LastCellInTree = 62, //Activates the cell in the last logical column for the last visible UltraTreeNode displayed by the UltraTree control.
            LastNode = 8,   //Move to Last Node In Tree
            LastNodeNoSelect = 16,   //Move to Last Node without altering the Selection
            //NextCell = 53,   //Activates the cell in the next logical column.
            //NextCellEnterEditMode = 63,  //Activates the cell in the next logical column and enters edit mode on that cell.
            NextNode = 1,   //Move to Next Node
            NextNodeNoSelect = 14,   //Move to Next Node without altering the Selection
            //NoAction = 52,   //Performs no action. This action is used so that a key will be considered to be available for processing by the control and will therefore result in the key events of the control being invoked but does not cause any action to occur.
            //PageDown = 11,   //Page Down In Tree
            //PageDownNoSelect = 18,   //Page Down in the Tree without altering the Selection
            //PageUp = 12, //Page Up In Tree
            //PageUpNoSelect = 17, //Page Up in the Tree without altering the Selection
            ParentNode = 42, //Move to the Parent of the Active Node
            //Paste = 51,  //Appends nodes from the clipboard to the UltraTree.ActiveNode's UltraTreeNode.Nodes collection.
            //PreviousCell = 54,   //Activates the cell in the previous logical column.
            //PreviousCellEnterEditMode = 64,  //Activates the cell in the previous logical column and enters edit mode on that cell.
            PrevNode = 2,   //Move to Previous Node
            PrevNodeNoSelect = 13,   //Move to Previous Node without altering the Selection
            //ScrollBottom,   //Scroll to the bottom of the tree without altering the selection or the active node.
            //ScrollDown, //Scroll the tree down one node without altering the selection or the active node.
            //ScrollPageDown, //Scroll down one page without altering the selection or the active node.
            //ScrollPageUp,   //Scroll up one page without altering the selection or the active node.
            //ScrollTop,  //Scroll to the top of the tree without altering the selection or the active node.
            //ScrollUp,   //Scroll the tree up one node without altering the selection or the active node.
            SelectActiveNode = 47,   //Selects the currently active node
            //SelectNextControl = 69,  //Selects the next control in the tab order.
            //SelectPreviousControl = 70,  //Selects the next control in the tab order.
            ToggleCheckbox = 41, //Toggle Check Box state on the Active Node
            ToggleSelection = 33,    //Toggles selection of the Active Node
            //UndoCellEdit = 67,   //Ends the edit mode session for the UltraTree.ActiveCell, discarding changes made during the edit mode session.
            //UndoNodeEdit = 68    //Cancels pending changes to cell values for the UltraTree.ActiveNode.
        }

        private Control _refobj;
        TreeNodesCollection _nodes; // If something wrong & _refobj refresh, Nodes will change ? Or if not what will happen. Because Nodes is a property of this _refobj so no need to call reflector everytime.

        public UltraTreeView(Control refUltraTreeView)
        {
            _refobj = refUltraTreeView;
            ReflectType.InitFromObject(_refobj); // Init first then use forever
            // Nodes is a property of _refobj so can declare a private _nodes & use parrallel with _refobj
            if (_refobj.InvokeRequired) _nodes = new TreeNodesCollection(_refobj.Invoke((Func<object>)delegate { return DienLib.Reflector.GetProperty<object>(_refobj, "Nodes"); }), _refobj);
            else _nodes = new TreeNodesCollection(DienLib.Reflector.GetProperty<object>(_refobj, "Nodes"), _refobj); // Does it really need Invoke ?
        }

        public UltraTreeNode ActiveNode
        {
            get
            {
                if (_refobj.InvokeRequired) return new UltraTreeNode(_refobj.Invoke((Func<object>)delegate { return DienLib.Reflector.GetProperty<object>(_refobj, "ActiveNode"); }), _refobj);
                else return new UltraTreeNode(DienLib.Reflector.GetProperty<object>(_refobj, "ActiveNode"), _refobj); // Does it really need Invoke ?
            }
            set
            {
                if (_refobj.InvokeRequired) _refobj.Invoke((MethodInvoker)delegate { DienLib.Reflector.SetProperty<object>(_refobj, "ActiveNode", value.Wrappee); });
                else DienLib.Reflector.SetProperty<object>(_refobj, "ActiveNode", value.Wrappee); // Does it really need Invoke ?
            }
        }

        public TreeNodesCollection Nodes
        {
            get
            {
                return _nodes;
                // No need to call Reflector everytime because Nodes is a property of _refobj. Use a private var instead.
                //if (_refobj.InvokeRequired) return new TreeNodesCollection(_refobj.Invoke((Func<object>)delegate { return DienLib.Reflector.GetProperty<object>(_refobj, "Nodes"); }), _refobj);
                //else return new TreeNodesCollection(DienLib.Reflector.GetProperty<object>(_refobj, "Nodes"), _refobj); // Does it really need Invoke ?
            }
        }

        public bool IsUpdating
        {
            get
            {
                if (_refobj.InvokeRequired) return (bool)_refobj.Invoke((Func<bool>)delegate { return DienLib.Reflector.GetProperty<bool>(_refobj, "IsUpdating"); });
                else return DienLib.Reflector.GetProperty<bool>(_refobj, "IsUpdating");
            }
        }

        public bool PerformAction(UltraTreeAction actionCode)
        {
            if (_refobj.InvokeRequired) return (bool)_refobj.Invoke((Func<bool>)delegate { return DienLib.Reflector.DoMethod<bool>(_refobj, "PerformAction", new object[] { actionCode, false, false }); });
            else return DienLib.Reflector.DoMethod<bool>(_refobj, "PerformAction", new object[] { actionCode, false, false });
        }

        public void ExpandAll()
        {
            if (_refobj.InvokeRequired) _refobj.Invoke((MethodInvoker)delegate { DienLib.Reflector.DoOverloadMethod(_refobj, "ExpandAll", new Type[0]); });
            else DienLib.Reflector.DoOverloadMethod(_refobj, "ExpandAll", new Type[0]);
        }

        public void CollapseAll()
        {
            if (_refobj.InvokeRequired) _refobj.Invoke((MethodInvoker)delegate { DienLib.Reflector.DoMethod(_refobj, "CollapseAll"); });
            else DienLib.Reflector.DoMethod(_refobj, "CollapseAll");
        }

        public int GetNodeCount(bool includeSubTrees)
        {
            if (_refobj.InvokeRequired) return (int)_refobj.Invoke((Func<int>)delegate { return DienLib.Reflector.DoMethod<int>(_refobj, "GetNodeCount", new object[] { includeSubTrees }); });
            else return DienLib.Reflector.DoMethod<int>(_refobj, "GetNodeCount", new object[] { includeSubTrees });
        }

        public bool PerformDoubleClick(UltraTreeNode node)
        {
            //FieldInfo[] finfos = _refobj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            //DienLib.PopUp.PopUpTreeView.ShowDialog("Public | NonPublic | Instance Fields :", _refobj.GetType().ToString(), finfos);

            //System.Reflection.MethodInfo medinfo = simlast.GetType().GetMethod("GetDescendant", new Type[] { typeof(Type), typeof(object) }); // Overload Method have to point out
            //System.Reflection.MethodInfo[] medinfos = simlast.GetType().GetMethods();


            // Prepare Dummy UIElement
            UIElement simlast = node.UIElement; //DienLib.Reflector.GetProperty<object>(node.Wrappee, "UIElement");              // object = Infragistics.Win.UltraWinTree.TreeNodeUIElement
            if (simlast == null) { PopUp.PopUpOK.ShowDialog("Node : \"{0}\"\r\nnot visible to Click !", "CANNOT DOUBLE CLICK INVISIBLE NODE ..."); return false; }
            simlast = simlast.GetDescendant(ReflectType.NodeSelectableAreaUIElement); // object = Infragistics.Win.UIElement as Win.UltraWinTree.NodeSelectableAreaUIElement
            if (simlast == null) { PopUp.PopUpOK.ShowDialog("Node : \"{0}\"\r\nnot visible to Click !", "CANNOT DOUBLE CLICK INVISIBLE NODE ..."); return false; }
            // Node Visisble to Double Click => Then Click
            Rectangle rect = simlast.Control.RectangleToScreen(simlast.Rect); 
            int X = rect.X + rect.Width / 2; int Y = rect.Y + rect.Height / 2;
            //DienLib.WinAPI.MouseDoubleClick(X, Y);
            MouseKeyboardLibrary.MouseSimulator.Position = new Point(X, Y);
            MouseKeyboardLibrary.MouseSimulator.DoubleClick(MouseKeyboardLibrary.MouseButton.Left);

            // IMPLEMENT check if DOUBLE CLICK sucess
            return true;
#if false
            // Set "lastElementEntered" var by Reflection
            object element = DienLib.Reflector.GetBasedField(ReflectType.UltraTree, _refobj, "element");    // object = Infragistics.Win.UltraWinTree.UltraTreeUIElement
            DienLib.Reflector.SetBasedField(ReflectType.ControlUIElementBase, element, "lastElementEntered", simlast.Wrappee);

            // Recheck succesfully then call OnDoubleClick
            UIElement checklast = this.UIElement.LastElementEntered;
            
            // Process OnDoubleClick
            if (checklast.Wrappee.GetType() == ReflectType.NodeSelectableAreaUIElement)
            {
                /*Rectangle rect = checklast.Control.RectangleToScreen(checklast.Rect);
                int X = rect.X + rect.Width / 2; int Y = rect.Y + rect.Height / 2;*/
                //object[] simargs = new object[] { new MouseEventArgs(MouseButtons.Left, 2, X, Y, 0) };
                //ReflectType.UltraTree.GetMethod("OnMouseDoubleClick", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(_refobj, simargs);
                //MessageBox.Show("Just call On Double Click !");
            }
#endif
        }

        /// <summary>
        /// return object as Infragistic.Win.UIElement
        /// </summary>
        public UIElement UIElement
        {
            get
            {
                if (_refobj.InvokeRequired) return new UIElement(_refobj.Invoke((Func<object>)delegate { return DienLib.Reflector.GetProperty<object>(_refobj, "UIElement"); }), _refobj);
                else return new UIElement(DienLib.Reflector.GetProperty<object>(_refobj, "UIElement"), _refobj);
            }
        }

        public void FindDuplicate()
        {

        }

        #region DISPOSE METHOD
        // Dispose() calls Dispose(true)
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        // NOTE: Leave out the finalizer altogether if this class doesn't 
        // own unmanaged resources itself, but leave the other methods
        // exactly as they are. 
        ~UltraTreeView()
        {
            // Finalizer calls Dispose(false)
            Dispose(false);
        }

        // The bulk of the clean-up code is implemented in Dispose(bool)
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources, (detach event ?)
                if (this._refobj != null) this._refobj = null; // Khong keu this._refobj.Dispose vi Reference toi object do. Ngoai ra do pass reference nen this._refobj = null khong anh huong gi toi object goc
                if (this._nodes != null) this._nodes = null;
                //if (this.a_LabelUnit != null) this.a_LabelUnit = null;
            }

            /* free native resources if there are any.
            if (nativeResource != IntPtr.Zero) 
            {
                Marshal.FreeHGlobal(nativeResource);
                nativeResource = IntPtr.Zero;
            }*/
        }
        #endregion


        #region TEST_METHOD
#if DEBUG
        /// <summary>
        /// Static Test Method for DienLib.SummitLib.Command.InjectSummitTest.
        /// </summary>
        public static void UltraTreeActionTest()
        {
            Form _form = SummitLib.Command.GetSummitMainForm();

            if (_form == null) throw new NullReferenceException();
            if (_form.IsDisposed) throw new Exception("New Job Form already Disposed or Closed");

            /*a_ProgressBar = DienLib.Reflector.GetField<ToolStripProgressBar>(_form, PROGRESS_BAR);
            a_tsNewJob = DienLib.Reflector.GetField<ToolStripSplitButton>(_form, BUT_NEWJOB);
            a_tsExport = DienLib.Reflector.GetField<ToolStripButton>(_form, BUT_EXPORT);
            a_tsExit = DienLib.Reflector.GetField<ToolStripButton>(_form, BUT_EXIT);*/
            UltraExplorerBar a_ultraExplorerBar1 = new UltraExplorerBar(DienLib.Reflector.GetField<Control>(_form, SummitFormLib.CUSTOMERVIEW_TOPPARENT));
            a_ultraExplorerBar1.PerformCustomerClick(Enums.Customer.PTTEP);

            SplitContainer a_splitContainer1 = DienLib.Reflector.GetField<SplitContainer>(_form, SummitFormLib.SPLIT_CONTAINER1);
            UltraTreeView a_treeJobPackage = new UltraTreeView(a_splitContainer1.Panel2.Controls[0].Controls[0].Controls[0]);
            //a_treeJobPackage.Nodes[1];
        }

        public static void UltraTreeViewTest()
        {
            Form _form = SummitLib.Command.GetSummitMainForm();

            if (_form == null) throw new NullReferenceException();
            if (_form.IsDisposed) throw new Exception("New Job Form already Disposed or Closed");

            /*a_ProgressBar = DienLib.Reflector.GetField<ToolStripProgressBar>(_form, PROGRESS_BAR);
            a_tsNewJob = DienLib.Reflector.GetField<ToolStripSplitButton>(_form, BUT_NEWJOB);
            a_tsExport = DienLib.Reflector.GetField<ToolStripButton>(_form, BUT_EXPORT);
            a_tsExit = DienLib.Reflector.GetField<ToolStripButton>(_form, BUT_EXIT);*/
            UltraExplorerBar a_ultraExplorerBar1 = new UltraExplorerBar(DienLib.Reflector.GetField<Control>(_form, SummitFormLib.CUSTOMERVIEW_TOPPARENT));
            a_ultraExplorerBar1.PerformCustomerClick(Enums.Customer.PTTEP);

            SplitContainer a_splitContainer1 = DienLib.Reflector.GetField<SplitContainer>(_form, SummitFormLib.SPLIT_CONTAINER1);
            UltraTreeView a_treeJobPackage = new UltraTreeView(a_splitContainer1.Panel2.Controls[0].Controls[0].Controls[0]);

            UltraTreeNode node = a_treeJobPackage.Nodes[10];
            //UltraTreeNode node2 = node.Nodes[0]; // Loading if Nodes[0] still not open yet
            a_treeJobPackage.ActiveNode = node;
            a_treeJobPackage.PerformAction(UltraTreeAction.ExpandNode);
            UltraTreeNode subnode = node.Nodes[0];
            a_treeJobPackage.ActiveNode = subnode;
            a_treeJobPackage.PerformAction(UltraTreeAction.SelectActiveNode);
            a_treeJobPackage.PerformDoubleClick(subnode);

            //MessageBox.Show(a_treeJobPackage.ActiveNode.Text);
        }
#endif
        #endregion
    }
}